package jm.easyconfig;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

class ConfigItemCore {
	public String item_profile = "default";
	public String item_code;
	public String item_value;
	public int item_status;

	ConfigItemCore load(Map<String, Object> map) {
		item_profile = (String) map.get("item_profile");
		item_code = (String) map.get("item_code");
		item_value = (String) map.get("item_value");
		item_status = (Integer) map.get("item_status");
		return this;
	}

	@Override
	public String toString() {
		return "ConfigItemCore [item_profile=" + item_profile + ", item_code=" + item_code + ", item_value="
				+ item_value + ", item_status=" + item_status + "]";
	}

	boolean deleted() {
		return isDeleted(this.item_status);
	}

	static boolean isDeleted(Object status) {
		if (status == null)
			return false;

		int st = (Integer) status;
		return (st & 0x1) == 1;
	}

	static int markDeleted(Object status) {
		if (status == null)
			return 1;

		int st = (Integer) status;
		return (st | 0x1);
	}

	boolean encrypted() {
		return (item_status & 0x2) == 2;
	}

	static String key(String profile, String code) {
		return profile + "+" + code;
	}

	String key() {
		return key(this.item_profile, this.item_code);
	}

	ConfigItemCore withProfile(String profile) {
		this.item_profile = profile;
		return this;
	}

	ConfigItemCore withCode(String code) {
		this.item_code = code;
		return this;
	}

	ConfigItemCore withValue(String value) {
		this.item_value = value;
		return this;
	}

	ConfigItemCore withStatus(int status) {
		this.item_status = status;
		return this;
	}

	ConfigItemCore withDeleted() {
		this.item_status = ConfigItemCore.markDeleted(this.item_status);
		return this;
	}
}

class ConfigItem extends ConfigItemCore {
	public String item_name;
	public Map<String, Object> item_schema;
	public Long updated_time;
	public String updated_user;
	public String updated_history;

	boolean equalMain(ConfigItem that) {
		return Objects.equals(this.item_profile, that.item_profile) && Objects.equals(this.item_code, that.item_code)
				&& Objects.equals(this.item_name, that.item_name) && Objects.equals(this.item_value, that.item_value)
				&& Objects.equals(this.item_status, that.item_status)
				&& Objects.equals(this.item_schema, that.item_schema);
	}

	@SuppressWarnings("unchecked")
	ConfigItem load(Map<String, Object> map) {
		super.load(map);

		item_name = (String) map.get("item_name");

		Object schema = map.get("item_schema");
		item_schema = schema instanceof Map ? (Map<String, Object>) schema
				: CfgHelper.jsonRead(schema == null ? "{}" : schema.toString());

		updated_time = (Long) map.get("updated_time");

		updated_user = (String) map.get("updated_user");

		updated_history = (String) map.get("updated_history");

		return this;
	}
}

class ActionConfigItem extends ConfigItem {
	public ActionType action;

	ActionConfigItem load(Map<String, Object> map) {
		super.load(map);
		action = (ActionType) map.get("action");
		return this;
	}

	ActionConfigItem load(ConfigItem item) {
		this.load(CfgHelper.jsonRead(CfgHelper.jsonWrite(item)));
		return this;
	}
}

enum ActionType {
	created, updated, setup, deleted
}

class ChangeLogItem {
	public String prof;
	public String code;
	public String act;
	public String nval;
	public String oval;
	public int nstat;
	public int ostat;
	public Map<String, Object> nschema;
	public Map<String, Object> oschema;
	public String nname;
	public String oname;
	public long otms;

	@Override
	public String toString() {
		return CfgHelper.jsonWrite(this);
	}

	String key() {
		return this.prof + "+" + this.code;
	}

	ChangeLogItem withProfile(String profile) {
		this.prof = profile;
		return this;
	}

	ChangeLogItem withCode(String code) {
		this.code = code;
		return this;
	}

	ChangeLogItem withAct(String act) {
		this.act = act;
		return this;
	}

	ChangeLogItem withNval(String nval) {
		this.nval = nval;
		return this;
	}

	ChangeLogItem withOval(String oval) {
		this.oval = oval;
		return this;
	}

	ChangeLogItem withNstat(int nstat) {
		this.nstat = nstat;
		return this;
	}

	ChangeLogItem withOstat(int ostat) {
		this.ostat = ostat;
		return this;
	}

	ChangeLogItem withNname(String nname) {
		this.nname = nname;
		return this;
	}

	ChangeLogItem withOname(String oname) {
		this.oname = oname;
		return this;
	}

	ChangeLogItem withNschema(Map<String, Object> nschema) {
		this.nschema = nschema;
		return this;
	}

	ChangeLogItem withOschema(Map<String, Object> oschema) {
		this.oschema = oschema;
		return this;
	}

	ChangeLogItem withOtms(Long otms) {
		this.otms = otms == null ? 0 : otms;
		return this;
	}

}

class ItemUpdatedHistory {
	public int count;
	public LinkedList<ItemUpdatedLog> list;

	static ItemUpdatedHistory empty() {
		ItemUpdatedHistory one = new ItemUpdatedHistory();
		one.list = new LinkedList<ItemUpdatedLog>();
		return one;
	}

	static ItemUpdatedHistory load(String jsonText) {
		return jsonText == null ? empty() : CfgHelper.jsonRead(jsonText, ItemUpdatedHistory.class);
	}

	@Override
	public String toString() {
		return CfgHelper.jsonWrite(this);
	}

	ItemUpdatedHistory insert(ItemUpdatedLog record) {
		this.count++;
		list.addFirst(record);
		while (list.size() > SysProps.inst.itemHistoryMax) {
			list.removeLast();
		}
		return this;
	}

}

class ItemUpdatedLog {
	public long id;
	public String act;
	public String val;
	public int stat;
	public String user;
	public String time;

	ItemUpdatedLog withId(long changedId) {
		this.id = changedId;
		return this;
	}

	ItemUpdatedLog withAct(String act) {
		this.act = act;
		return this;
	}

	ItemUpdatedLog withVal(Object val) {
		this.val = val == null ? "" : val.toString();
		return this;
	}

	ItemUpdatedLog withStat(int stat) {
		this.stat = stat;
		return this;
	}

	ItemUpdatedLog withStat(Object stat) {
		this.stat = stat == null ? 0 : Integer.parseInt(stat.toString());
		return this;
	}

	ItemUpdatedLog withUser(String user) {
		this.user = user;
		return this;
	}

	ItemUpdatedLog withTime(String time) {
		this.time = time;
		return this;
	}

}

class PageArgs {
	public int pageSize = 10;
	public int pageNo = 1;
}

class PagerResult<T> {
	public long totalCount;
	public T list;
}

class RestoreArgs {
	public String remark;
	public int changeId;

	RestoreArgs withRemark(String remark) {
		this.remark = remark;
		return this;
	}

	RestoreArgs withChangeId(int changeId) {
		this.changeId = changeId;
		return this;
	}
}

class UpdateSourceArgs {
	public String remark;
	private ActionConfigItem[][] listArray;
	public boolean gray;

	/*
	 * setList and getList are for JSON converter to deal with request from
	 * frontend.
	 */
	public void setList(ActionConfigItem[] list) {
		this.withList(list);
	}

	public ActionConfigItem[] getList() {
		if (listArray == null)
			return null;

		if (listArray.length < 1)
			return null;

		return listArray[0];
	}

	public ActionConfigItem[][] listArray() {
		return this.listArray;
	}

	UpdateSourceArgs withRemark(String remark) {
		this.remark = remark;
		return this;
	}

	UpdateSourceArgs withList(ActionConfigItem[] list) {
		this.listArray = new ActionConfigItem[][] { list };
		return this;
	}

	UpdateSourceArgs withListArray(ActionConfigItem[][] listArray) {
		this.listArray = listArray;
		return this;
	}

	UpdateSourceArgs withAction(ActionConfigItem... action) {
		this.listArray = new ActionConfigItem[][] { action };
		return this;
	}
}

class UpdateSourceReturn {
	public int changedId;
	public ConfigChangeItem changeItem;
	public List<ConfigItemCore> updatedResult;

	UpdateSourceReturn withChangedId(int changedId) {
		this.changedId = changedId;
		return this;
	}

	UpdateSourceReturn withChangedItem(ConfigChangeItem changeItem) {
		this.changeItem = changeItem;
		return this;
	}

	UpdateSourceReturn withUpdatedResult(Collection<ConfigItemCore> updatedResult) {
		List<ConfigItemCore> lst = new LinkedList<ConfigItemCore>();
		lst.addAll(updatedResult);
		return withUpdatedResult(lst);
	}

	UpdateSourceReturn withUpdatedResult(List<ConfigItemCore> updatedResult) {
		this.updatedResult = updatedResult;
		return this;
	}

	static UpdateSourceReturn of(Collection<ConfigItemCore> updatedResult) {
		UpdateSourceReturn ret = new UpdateSourceReturn();
		return ret.withUpdatedResult(updatedResult);
	}
}

enum ChangeStatus {
	published, terminated, graying, stored, canceled, planed
}

class ConfigChangeItem {
	public int changed_id;
	public ChangeSummary changed_summary;
	public ChangeLogItem[] changed_content;
	public String changed_user;
	public Long changed_time;
	public int changed_status;

	String contentToString() {
		return CfgHelper.jsonWrite(this.changed_content);
	}

	ConfigChangeItem load(Map<String, Object> map) {
		if (map.containsKey("changed_id"))
			this.changed_id = (Integer) map.get("changed_id");
		if (map.containsKey("changed_summary"))
			this.changed_summary = CfgHelper.jsonRead(map.get("changed_summary").toString(), ChangeSummary.class);
		if (map.containsKey("changed_content"))
			this.changed_content = CfgHelper.jsonRead(map.get("changed_content").toString(), ChangeLogItem[].class);
		if (map.containsKey("changed_user"))
			this.changed_user = map.get("changed_user").toString();
		if (map.containsKey("changed_time"))
			this.changed_time = (Long) map.get("changed_time");
		if (map.containsKey("changed_status"))
			this.changed_status = (Integer) map.get("changed_status");
		return this;
	}

	ConfigChangeItem withStatus(ChangeStatus status) {
		this.changed_status = status.ordinal();
		return this;
	}

	ChangeStatus status() {
		return ChangeStatus.values()[this.changed_status];
	}
}

class ChangeSummary {
	public int count;
	public String[] profiles;
	public List<ChangeRemark> remarks;
	public List<NodePublish> nodes;

	ChangeSummary addRemark(ChangeStatus action, String remark) {
		return addRemark(action.name(), remark);
	}

	ChangeSummary addRemark(String action, String remark) {
		LinkedList<ChangeRemark> lst = new LinkedList<ChangeRemark>();
		if (this.remarks != null)
			lst.addAll(this.remarks);

		long now = CfgHelper.unixMsOfNow();

//		CfgContextEnvironmentAware aware = CfgContextEnvironmentAware.getSharedInstance();
//		String user = aware != null ? aware.contextUser() : "";

		lst.addFirst(new ChangeRemark().withRemark(remark).withTime(now).withAction(action)
				.withUser(CfgAdminServlet.threadContext().username())
				.withIp((String) CfgAdminServlet.threadContext().get("clientIp")));
		this.remarks = lst;

		return this;
	}

	ChangeSummary addNodes(List<String> nodeNames) {
		LinkedList<NodePublish> lst = new LinkedList<NodePublish>();
		if (this.nodes != null)
			lst.addAll(this.nodes);

		long now = CfgHelper.unixMsOfNow();

//		CfgContextEnvironmentAware aware = CfgContextEnvironmentAware.getSharedInstance();
//		String user = aware != null ? aware.contextUser() : "";

		nodeNames.forEach(n -> {
			lst.addFirst(
					new NodePublish().withNode(n).withTime(now).withUser(CfgAdminServlet.threadContext().username()));
		});

		this.nodes = lst;

		return this;
	}

	ChangeSummary withCount(int count) {
		this.count = count;
		return this;
	}

	ChangeSummary withProfiles(String[] profiles) {
		this.profiles = profiles;
		return this;
	}

	ChangeSummary withProfiles(List<ChangeLogItem> logList) {
		HashMap<String, String> profs = new HashMap<String, String>();
		for (ChangeLogItem log : logList) {
			profs.put(log.prof, log.prof);
		}

		this.profiles = profs.keySet().toArray(new String[profs.keySet().size()]);

		return this;
	}

	@Override
	public String toString() {
		return CfgHelper.jsonWrite(this);
	}

}

class NodePublish {
	public String name;
	public Long time;
	public String user;

	NodePublish withNode(String name) {
		this.name = name;
		return this;
	}

	NodePublish withTime(Long time) {
		this.time = time;
		return this;
	}

	NodePublish withUser(String user) {
		this.user = user;
		return this;
	}
}

class ChangeRemark {
	public String action;
	public String remark;
	public Long time;
	public String user;
	public String ip;

	ChangeRemark withAction(String action) {
		this.action = action;
		return this;
	}

	ChangeRemark withRemark(String remark) {
		this.remark = remark;
		return this;
	}

	ChangeRemark withTime(Long time) {
		this.time = time;
		return this;
	}

	ChangeRemark withUser(String user) {
		this.user = user;
		return this;
	}

	ChangeRemark withIp(String ip) {
		this.ip = ip;
		return this;
	}
}
